package org.example.fastproject.web.sys.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.fastproject.core.bean.PageParam;
import org.example.fastproject.core.bean.SysUserDetails;
import org.example.fastproject.core.enums.RoleEnum;
import org.example.fastproject.core.exception.Asserts;
import org.example.fastproject.core.utils.JwtUtil;
import org.example.fastproject.core.utils.SecurityUtil;
import org.example.fastproject.core.utils.TreeUtil;
import org.example.fastproject.web.sys.model.SysUserRole;
import org.example.fastproject.web.sys.model.SysUser;
import org.example.fastproject.web.sys.mapper.SysUserMapper;
import org.example.fastproject.web.sys.model.dto.user.*;
import org.example.fastproject.web.sys.model.vo.menu.MenuNode;
import org.example.fastproject.web.sys.model.vo.user.UserVo;
import org.example.fastproject.web.sys.service.SysCacheService;
import org.example.fastproject.web.sys.service.SysPermissionService;
import org.example.fastproject.web.sys.service.SysUserRoleService;
import org.example.fastproject.web.sys.service.SysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author 王海舟
 * @since 2023-01-16 11:54:01
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService, UserDetailsService {
    @Resource
    private PasswordEncoder encoder;

    @Resource
    private SysCacheService cacheService;

    @Resource
    private SysUserRoleService userRoleService;

    @Resource
    private SysPermissionService permissionService;

    @Value("${login.verify.enable}")
    private boolean enable = false;


    @Override
    public String login(UserLoginDto dto) {
        SysUserDetails user = (SysUserDetails) this.loadUserByUsername(dto.getName());
        if(Objects.isNull(user))
            Asserts.fail("用户名错误");
        boolean success = encoder.matches(dto.getPassword(),user.getPassword());
        if(!success)
            Asserts.fail("密码错误");
        if(enable){
            String key = dto.getKey();
            String code = dto.getCode();
            if(!StrUtil.isAllNotEmpty(key,code))
                Asserts.fail("验证码不能为空");
            String verifyCode = cacheService.getVerify(key);
            if(StrUtil.isEmpty(verifyCode))
                Asserts.fail("验证码已失效");
            if(!StrUtil.equals(verifyCode,code))
                Asserts.fail("验证码错误");
        }
        //认证信息存入security上下文
        /**
        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);*/
        //缓存用户信息
        Long userId = user.getId();
        String token = JwtUtil.getToken(userId);
        cacheService.setLogin(userId,token);
        return token;
    }

    @Override
    @Transactional
    public Long register(UserRegisterDto dto) {
        //检验身份证号码是否已存在
        SysUser user = this.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getIdCart,dto.getIdCart()));
        if(Objects.nonNull(user))
            Asserts.fail("身份证号码已存在");
        //检验用户名是否已存在
        user = this.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getName,dto.getName()));
        if(Objects.nonNull(user))
            Asserts.fail("用户名已存在");
        //创建用户对象
        user = BeanUtil.toBean(dto, SysUser.class);
        user.setPassword(encoder.encode(dto.getPassword()));
        this.save(user);
        //回写用户信息
        Long userId = user.getId();
        Date now = new Date();
        user.setCreateUser(userId);
        user.setCreateTime(now);
        user.setUpdateUser(userId);
        user.setUpdateTime(now);
        this.updateById(user);
        //创建用户角色关联
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setUserId(userId);
        sysUserRole.setRoleId(RoleEnum.USER.getRoleId());
        sysUserRole.setCreateUser(userId);
        sysUserRole.setCreateTime(now);
        sysUserRole.setUpdateUser(userId);
        sysUserRole.setUpdateTime(now);
        userRoleService.save(sysUserRole);
        //返回用户id
        return userId;
    }

    @Override
    public void logout() {
        Long userId = SecurityUtil.getUser().getId();
        //删除相关缓存
        cacheService.removeLogin(userId);
        cacheService.removeUser(userId);
        cacheService.removePermission(userId);
        cacheService.removeMenuTree(userId);
    }

    @Override
    public List<MenuNode> getTree() {
        Long userId = SecurityUtil.getUser().getId();
        List<MenuNode> tree = cacheService.getMenuTree(userId);
        if(CollectionUtil.isEmpty(tree)){
            List<MenuNode> list = this.baseMapper.selectMenu(userId);
            tree = TreeUtil.toTree(list);
            cacheService.setMenuTree(userId,tree);
        }
        return tree;
    }

    @Override
    public Page<UserVo> page(PageParam param) {
        Page page = new Page(param.getNum(),param.getSize());
        this.page(page);
        List<SysUser> list = page.getRecords();
        List<UserVo> users = list.stream().map(sysUser -> {
            return BeanUtil.toBean(sysUser,UserVo.class);
        }).collect(Collectors.toList());
        page.setRecords(users);
        return page;
    }

    @Override
    @Transactional
    public Long save(UserInsertDto dto) {
        //检验身份证号码是否已存在
        SysUser sysUser = this.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getIdCart,dto.getIdCart()));
        if(Objects.nonNull(sysUser))
            Asserts.fail("身份证号码已存在");
        //检验用户名是否已存在
        sysUser = this.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getName,dto.getName()));
        if(Objects.nonNull(sysUser))
            Asserts.fail("用户名已存在");
        SysUserDetails user = SecurityUtil.getUser();
        //创建用户对象
        sysUser = BeanUtil.toBean(dto, SysUser.class);
        sysUser.setPassword(encoder.encode(dto.getPassword()));
        sysUser.setCreateUser(user.getId());
        sysUser.setCreateTime(user.getDate());
        sysUser.setUpdateUser(user.getId());
        sysUser.setUpdateTime(user.getDate());
        this.save(sysUser);
        Long userId = sysUser.getId();
        //创建用户角色关联
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setUserId(userId);
        sysUserRole.setRoleId(RoleEnum.USER.getRoleId());
        sysUserRole.setCreateUser(user.getId());
        sysUserRole.setCreateTime(user.getDate());
        sysUserRole.setUpdateUser(user.getId());
        sysUserRole.setUpdateTime(user.getDate());
        userRoleService.save(sysUserRole);
        //返回用户id
        return userId;


    }

    @Override
    @Transactional
    public boolean remove(Long id) {
        boolean success = this.removeById(id);
        if(success){
            //删除相关缓存
            cacheService.removeLogin(id);
            cacheService.removeUser(id);
            cacheService.removeMenuTree(id);
            cacheService.removePermission(id);
        }
        return success;
    }

    @Override
    @Transactional
    public boolean update(UserUpdateDto dto) {
        //检验身份证号码是否已存在
        SysUser sysUser = this.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getIdCart,dto.getIdCart())
                .ne(SysUser::getId,dto.getId()));
        if(Objects.nonNull(sysUser))
            Asserts.fail("身份证号码已存在");
        //检验用户名是否已存在
        sysUser = this.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getName,dto.getName())
                .ne(SysUser::getId,dto.getId()));
        if(Objects.nonNull(sysUser))
            Asserts.fail("用户名已存在");
        sysUser = BeanUtil.toBean(dto, SysUser.class);
        String password = dto.getPassword();
        if(StrUtil.isNotEmpty(password)){
            password = encoder.encode(password);
            sysUser.setPassword(password);
        }
        boolean success = this.updateById(sysUser);
        if(success){
            //删除相关缓存
            Long userId = dto.getId();
            cacheService.removeUser(userId);
        }
        return success;
    }

    @Override
    public boolean role(UserRoleDto dto) {
        //删除已分配角色
        Long userId = dto.getUserId();
        userRoleService.remove(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId,userId));
        //删除相关缓存
        cacheService.removePermission(userId);
        //重新分配角色
        List<SysUserRole> list = dto.getRoleIds().stream().map(roleId -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setUserId(dto.getUserId());
            sysUserRole.setRoleId(roleId);
            return sysUserRole;
        }).collect(Collectors.toList());
        return userRoleService.saveBatch(list);
    }

    @Override
    public UserVo getUserById(Long userId) {
        UserVo user = cacheService.getUser(userId);
        if(Objects.isNull(user)){
            SysUser sysUser = this.getById(userId);
            user = BeanUtil.toBean(sysUser,UserVo.class);
            cacheService.setUser(userId,user);
        }
        return user;
    }

    @Override
    public Map<String, String> verify() {
        if(!enable)
            Asserts.fail("验证码功能已关闭");
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200,100);
        String code = lineCaptcha.getCode();
        String image = lineCaptcha.getImageBase64();
        String key = IdUtil.fastSimpleUUID();
        Map<String, String> data = new HashMap<>();
        data.put("key",key);
        data.put("image",image);
        cacheService.setVerify(key,code);
        return data;
    }

    @Override
    public UserVo info() {
        Long userId = SecurityUtil.getUser().getId();
        UserVo user = this.getUserById(userId);
        return user;
    }

    /**
     * 通过用户名获取用户信息（用户名，密码，权限集合）
     * @param name
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
        //获取用户信息
        SysUser sysUser = this.getOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getName,name));
        if(Objects.isNull(sysUser))
            Asserts.fail("用户不存在");
        UserVo user = BeanUtil.toBean(sysUser, UserVo.class);
        //获取用户权限列表
        List<String> permissions = permissionService.getByUserId(user.getId());
        return new SysUserDetails(user,permissions);
    }
}
